package co.edu.unal.bioingenium.kbmed.knowledge.similarity.main;

import co.edu.unal.bioingenium.kbmed.config.Configuration;
import co.edu.unal.bioingenium.kbmed.knowledge.dao.KnowledgeSourceDAO;
import co.edu.unal.bioingenium.kbmed.knowledge.dao.KnowledgeSourceDAOFactory;
import co.edu.unal.bioingenium.kbmed.knowledge.similarity.SemanticSimilartyService;
import co.edu.unal.bioingenium.kbmed.knowledge.similarity.SemanticSimilartyService.SemanticSimilarityFunction;
import co.edu.unal.bioingenium.kbmed.knowledge.similarity.vo.NeighborSimilarityComparator;
import co.edu.unal.bioingenium.kbmed.util.io.FileManager;
import gnu.trove.map.hash.TObjectFloatHashMap;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author Ing. Alejandro Riveros Cruz
 */
public class CalculateNeighborSimilarity {

    /**
     * 
     */
    private int similarityNeighbors;
    /**
     * 
     */
    private SemanticSimilartyService similartyService;
    /**
     * 
     */
    private KnowledgeSourceDAO knowledgeSourceDAO;
    /**
     * 
     */
    private SemanticSimilarityFunction similarityFunction;

    /**
     * 
     * @param similarityFunction
     * @param similarityNeighbors
     */
    public CalculateNeighborSimilarity(SemanticSimilarityFunction similarityFunction, int similarityNeighbors) {
        this.similarityFunction = similarityFunction;
        this.similarityNeighbors = similarityNeighbors;
        similartyService = SemanticSimilartyService.getInstance();
        knowledgeSourceDAO = KnowledgeSourceDAOFactory.getKnowledgeSourceDAO();
    }

    /**
     * 
     */
    public void buildNeighborsSimilarityIndex() {
        Set<String> categories = knowledgeSourceDAO.getCategories();
        for (String category : categories) {
            knowledgeSourceDAO.checkStatus();
            buildNeighborsSimilarityIndex(category);
        }
    }

    /**
     * 
     * @param category
     */
    public void buildNeighborsSimilarityIndex(String category) {
        Logger.getLogger(CalculateNeighborSimilarity.class.getName()).log(Level.INFO, "Calculating neighborg similarity values for category \"{0}\"", category);
        Map<String, TObjectFloatHashMap<String>> similarConceptsValuesMap = new HashMap<String, TObjectFloatHashMap<String>>();
        Map<String, List<String>> similarConceptsSortedMap = new HashMap<String, List<String>>();
        Set<String> conceptIdSet = knowledgeSourceDAO.getConceptsInCategory(category);
        List<String> conceptIdList = new ArrayList<String>(conceptIdSet);
        TObjectFloatHashMap<String> similarityMap;
        TObjectFloatHashMap<String> tempMap;
        List<String> sortedConcepts;
        float[] similarityValues;
        int c = 1;
        String relatedConceptId;
        float similarity;
        for (String conceptId : conceptIdSet) {
            System.out.println((c++) + "/" + conceptIdSet.size() + "   ConceptId=" + conceptId);
            tempMap = new TObjectFloatHashMap<String>(conceptIdList.size());
            similarityValues = similartyService.getSemanticSimilaritysByConceptIdsArray(conceptId, conceptIdList, similarityFunction);
            for (int i = 0; i < similarityValues.length; i++) {
                tempMap.put(conceptIdList.get(i), similarityValues[i]);
            }
            Collections.sort(conceptIdList, new NeighborSimilarityComparator(tempMap));

            similarityMap = new TObjectFloatHashMap<String>(similarityNeighbors);
            sortedConcepts = new ArrayList<String>();
            for (int i = 0; i < conceptIdList.size() && i < similarityNeighbors; i++) {
                relatedConceptId = conceptIdList.get(i);
                similarity = tempMap.get(relatedConceptId);
                if (similarity == 0) {
                    break;
                }
                sortedConcepts.add(relatedConceptId);
                similarityMap.put(relatedConceptId, similarity);
            }
            similarConceptsValuesMap.put(conceptId, similarityMap);
            similarConceptsSortedMap.put(conceptId, sortedConcepts);
        }
        saveSimilarityValues(category, similarConceptsValuesMap, similarConceptsSortedMap);
    }

    /**
     * 
     * @param category
     * @param similarConceptsValuesMap
     * @param similarConceptsSortedMap  
     */
    public void saveSimilarityValues(String category, Map<String, TObjectFloatHashMap<String>> similarConceptsValuesMap, Map<String, List<String>> similarConceptsSortedMap) {
        try {
            Logger.getLogger(CalculateNeighborSimilarity.class.getName()).log(Level.INFO, "Store similarity values \"{0}\"", category);
            String categoryStandard = FileManager.getStandardName(category);
            BufferedWriter informationContentValsBF = new BufferedWriter(new FileWriter(new File(Configuration.FEATURE_DIR + File.separator + Configuration.KNOWLEDGE_SOURCE_NAME.toLowerCase() + "-" + categoryStandard + "-" + similarityFunction + "-" + "NeighborgSimilarityValues.cvs")));
            TObjectFloatHashMap<String> tempMap;
            List<String> tempList;
            for (String conceptId : similarConceptsValuesMap.keySet()) {
                informationContentValsBF.write(conceptId);
                tempMap = similarConceptsValuesMap.get(conceptId);
                tempList = similarConceptsSortedMap.get(conceptId);
                for (String relatedConceptId : tempList) {
                    informationContentValsBF.write("," + relatedConceptId + "," + tempMap.get(relatedConceptId));
                }
                informationContentValsBF.write("\n");
            }
            informationContentValsBF.flush();
            informationContentValsBF.close();
        } catch (IOException ex) {
            Logger.getLogger(CalculateNeighborSimilarity.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    /**
     * 
     * @param args
     */
    public static void main(String[] args) {
        CalculateNeighborSimilarity cns = new CalculateNeighborSimilarity(SemanticSimilarityFunction.WP, 200);
        if (args.length == 0) {
            cns.buildNeighborsSimilarityIndex();
        } else {
            for (int i = 0; i < args.length; i++) {
                cns.buildNeighborsSimilarityIndex(args[i]);
            }
        }

    }
}
